package Java_Lab.Multithreading.SinCos;

import java.util.ArrayList;
import java.util.List;

public class SinCos {
	
	private volatile double result;
	
	private synchronized void addToResult(double i){
		result += i;
	}
	
	public double evaluate(int numberOfThreads, int range){
		int rangeForThread = (range*2 + 1) % numberOfThreads == 0 ?
				(range*2 + 1) / numberOfThreads :
				(range*2 + 1) / numberOfThreads + 1;
		List<Thread> threads = new ArrayList<Thread>();
		
		//distributing points for evaluation between threads
		for(int i = 0; i < numberOfThreads; i++){
			int from = i*rangeForThread - range;
			//check for situation when less points are given for the last thread
			int to = ((i+1)*rangeForThread-range) > range ?
					range:
					(i+1)*rangeForThread-range;
			Thread newThread = new Thread(this.new SinCosEvaluator(from, to));
			threads.add(newThread);
		}
		for(Thread thread : threads){
			thread.start();
		}
		//including the last point
		result += func(range);
		//The loop ensures that the main thread waits until all evaluations
		//are finished
		for(Thread thread : threads){
			try {
				thread.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return result;
	}
	
	private double func(double arg){
		return Math.sin(arg)*Math.cos(arg);
	}
	
	class SinCosEvaluator implements Runnable{
		
		private int from;
		private int to;
		
		private double localResult = 0;
		
		private final double step = 0.0001;
		
		SinCosEvaluator(int from, int to) {
			this.from = from;
			this.to = to;
		}

		public void run() {
			long precision = Math.round(1/step);
			//System.out.println("[ " + from + " , " + to + " )");
			for(double i = from; i < to; i += step){
				i = Math.round((i*precision))/(double)precision;
				localResult += func(i);
			}
			addToResult(localResult);
		}
		
	}
}
